import time
import serial
import struct
import json
from typing import Optional, Dict, List, Any
from PyQt6.QtCore import QThread, QObject, QMutex, pyqtSignal


class DataProtocol(QObject):
    """
    串口协议解析器 - 简单直接，不搞复杂的异常处理
    """
    error_occurred = pyqtSignal(str)    # 发送错误信息
    TERMINATOR = bytes([0xAA, 0xCC])  # 数据包终止符

    def __init__(self, command_file: str = 'config/command.json'):
        super().__init__()
        self.buffer = bytearray()  # 接收缓冲区
        self._config = self._load_config(command_file)
        self._processors = self._compile_processors()

    def _load_config(self, command_file: str) -> Dict[str, Any]:
        """加载配置文件 - 启动时验证，失败直接崩溃比运行时错误好"""
        try:
            with open(command_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
            if 'identifiers' not in config:
                raise ValueError("Missing 'identifiers' in config")
            return config
        except (FileNotFoundError, json.JSONDecodeError) as e:
            # 配置文件错误应该立即失败，不是运行时错误
            raise RuntimeError(f"Failed to load config: {e}")

    def _compile_processors(self) -> Dict[str, Dict[str, callable]]:
        """预编译所有数据处理器 - 启动时失败比运行时失败好"""
        processors = {}
        for identifier, cmd in self._config.get('identifiers', {}).items():
            field_processors = {}
            for field in cmd.get('fields', []):
                field_name = field['name']
                if 'processor' in field:
                    try:
                        field_processors[field_name] = eval(field['processor'])
                    except Exception as e:
                        raise RuntimeError(f"Failed to compile processor for {identifier}.{field_name}: {e}")
            processors[identifier] = field_processors
        return processors
    
    @staticmethod
    def calculate_crc8(data: bytes) -> int:
        """计算CRC8校验码 - 静态方法避免不必要的实例依赖"""
        polynomial = 0x07  # 标准CRC8多项式
        crc = 0x00
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x80:
                    crc = (crc << 1) ^ polynomial
                else:
                    crc <<= 1
                crc &= 0xFF  # 确保8位范围
        return crc

    def process_received_data(self, data: bytes) -> List[Optional[Dict[str, Any]]]:
        """
        处理接收到的字节流 - 可能包含多个数据包
        """
        self.buffer.extend(data)
        results = []
        
        # 循环处理缓冲区中的所有完整数据包
        while self.TERMINATOR in self.buffer:
            term_pos = self.buffer.find(self.TERMINATOR)
            packet_data = bytearray(self.buffer[:term_pos])
            self.buffer = bytearray(self.buffer[term_pos + len(self.TERMINATOR):])
            
            # 尝试解析数据包，失败就发错误信号
            results.append(self._parse_packet(packet_data))

        return results
    
    def _parse_packet(self, packet: bytearray) -> Optional[Dict[str, Any]]:
        """
        解析数据包 - 合并版本，直接返回字典
        """
        # 基本长度检查
        if len(packet) < 2:
            self.error_occurred.emit("数据包太短")
            return None

        identifier = f"0x{packet[0]:02X}"
        
        # 检查标识符是否已知
        if identifier not in self._config['identifiers']:
            self.error_occurred.emit(f"未知标识符: {identifier}")
            return None

        command = self._config['identifiers'][identifier]
        expected_length = command['data_length']
        
        # 检查数据长度
        if len(packet) != expected_length + 2:  # +2 for identifier and CRC
            self.error_occurred.emit(f"数据长度错误: 期望{expected_length + 2}字节，实际{len(packet)}字节")
            return None

        # 验证CRC
        data_section = packet[1:-1]
        received_crc = packet[-1]
        calculated_crc = self.calculate_crc8(packet[:-1])
        
        if calculated_crc != received_crc:
            self.error_occurred.emit(f"CRC校验失败: 计算值{calculated_crc}, 接收值{received_crc}")
            return None

        try:
            processors = self._processors.get(identifier, {})
            parsed_data = {}
            
            for field in command['fields']:
                field_name = field['name']
                field_type = field['type']
                offset = field['offset_byte']
                
                # 按类型解析
                if field_type == 'int16':
                    value = struct.unpack('<h', data_section[offset:offset + 2])[0]
                elif field_type == 'uint16':
                    value = struct.unpack('<H', data_section[offset:offset + 2])[0]
                elif field_type == 'uint32':
                    value = struct.unpack('<I', data_section[offset:offset + 4])[0]
                else:
                    raise ValueError(f"不支持的数据类型: {field_type}")
                
                # 应用处理器
                if field_name in processors:
                    value = processors[field_name](value)
                
                parsed_data[field_name] = value
            
            parsed_data['pc_timestamp'] = time.time_ns() / 1000000

            if identifier == '0xFF':
                # 错误码特殊处理
                error_code = parsed_data.get('error_code', 0)
                error_codes = self._config.get('identifiers', {}).get('0xFF', {}).get('error_codes', {})
                error_code_str = f"0x{error_code:04X}"
                error_message = error_codes.get(error_code_str, "未知错误")
                self.error_occurred.emit(f"设备错误 {error_code_str}: {error_message}")
                return None
            else:
                # 返回包含所有信息的字典
                return {
                    'identifier': identifier,
                    'data': parsed_data
                }
            
        except Exception as e:
            self.error_occurred.emit(f"字段解析失败: {e}")
            return None

    def pack_command(self, identifier: str, data_list: List[Any]) -> bytes:
        """
        将命令打包为字节流 - 发送端的逆向操作
        
        包格式：标识符(1字节) + 数据段(N字节) + CRC8(1字节) + 终止符(2字节)
        """
        if identifier not in self._config['identifiers']:
            raise ValueError(f"未知标识符: {identifier}")
        
        command = self._config['identifiers'][identifier]
        fields = command['fields']
        
        if len(fields) != len(data_list):
            raise ValueError(f"数据数量不匹配: 期望{len(fields)}项，实际{len(data_list)}项")
        
        processors = self._processors.get(identifier, {})
        data_bytes = b''
        
        # 逐字段打包数据
        for field, value in zip(fields, data_list):
            field_name = field['name']
            field_type = field['type']
            
            # 应用发送前处理器（通常是接收处理器的逆变换）
            processed_value = value
            if field_name in processors:
                processed_value = processors[field_name](value)
            
            # 按类型打包 - 小端序，与解析保持一致
            if field_type == 'int16':
                data_bytes += struct.pack('<h', int(processed_value))
            elif field_type == 'uint16':
                data_bytes += struct.pack('<H', int(processed_value))
            elif field_type == 'uint32':
                data_bytes += struct.pack('<I', int(processed_value))
            else:
                raise ValueError(f"不支持的数据类型: {field_type}")
        
        # 构建完整数据包
        identifier_byte = int(identifier, 16).to_bytes(1, 'little')
        crc_byte = self.calculate_crc8(identifier_byte + data_bytes).to_bytes(1, 'little')
        packet = identifier_byte + data_bytes + crc_byte + self.TERMINATOR
        
        return packet


class SerialThread(QThread):
    """
    串口通信线程 - 专注I/O，不处理业务逻辑
    """

    data_received = pyqtSignal(list)  # 发送数据包字典
    error_occurred = pyqtSignal(str)    # 发送错误信息

    def __init__(self, serial_port: serial.Serial, protocol: DataProtocol):
        super().__init__()
        self._serial = serial_port
        self._protocol = protocol
        self._running = True
        self._mutex = QMutex()  # 保护串口写操作的线程安全
        
        # 连接协议信号到线程信号
        self._protocol.error_occurred.connect(self.error_occurred.emit)

    def run(self):
        """主线程循环 - 简化版本"""
        if not hasattr(self._serial, 'cancel_read'):
            self._serial.timeout = 1

        while self._running and self._serial.is_open:
            try:
                if self._serial.in_waiting > 0:
                    # 一次性读取所有可用数据
                    data = self._serial.read(self._serial.in_waiting)
                    if data:
                        # 直接调用协议处理，信号会自动发出
                        self._mutex.lock()
                        self.data_received.emit(self._protocol.process_received_data(data))
                        self._mutex.unlock()
            except serial.SerialException as e:
                self.error_occurred.emit(f"串口错误: {e}")
                self._running = False
                break
            
            self.msleep(1)

    def write_command(self, identifier: str, data_list: List[Any]) -> bool:
        """发送命令到串口 - 简化版本"""
        try:
            self._mutex.lock()
            packet = self._protocol.pack_command(identifier, data_list)
            self._mutex.unlock()
            self._serial.write(packet)
            return True
        except Exception as e:
            self.error_occurred.emit(f"发送失败: {e}")
            return False

    def stop(self):
        """停止线程"""
        self._running = False
        if hasattr(self._serial, 'cancel_read'):
            self._serial.cancel_read()
        self.wait(1000)


class SerialService(QObject):
    """
    串口服务主接口 - 简化版本，用信号区分数据和错误
    
    设计哲学：
    - 0xFF标识符的数据包通过message_signal发送错误信息
    - 其他标识符的数据包通过data_signal发送
    - 统一处理，让调用者决定如何区分
    """

    data_signal = pyqtSignal(object)    # 发送正常数据包字典
    message_signal = pyqtSignal(str)    # 发送错误信息和状态消息

    def __init__(self):
        super().__init__()
        self._serial_port: Optional[serial.Serial] = None
        self._protocol = DataProtocol()
        self._thread: Optional[SerialThread] = None
       
    def open_port(self, port: str, baudrate: int) -> bool:
        """打开串口并启动通信 - 简化版本"""
        if self._thread and self._thread.isRunning():
            self.close_port()
        
        try:
            self._serial_port = serial.Serial(port, baudrate, timeout=0)
            self._thread = SerialThread(self._serial_port, self._protocol)
            
            # 连接信号
            self._thread.data_received.connect(self.data_signal.emit)
            self._thread.error_occurred.connect(self.message_signal.emit)
            
            self._thread.start()
            self.message_signal.emit(f"{port}串口打开，波特率{baudrate}")
            return True
            
        except Exception as e:
            self.message_signal.emit(f"{port}串口打开失败: {e}")
            self._cleanup()
            return False

    def close_port(self):
        """关闭串口连接"""
        if self._thread:
            self._thread.stop()
            self._thread = None
        
        if self._serial_port:
            self._serial_port.close()
            self._serial_port = None
        
        self.message_signal.emit("串口已关闭")

    def write_command(self, identifier: str, data_list: List[Any]) -> bool:
        """发送命令 - 简化接口"""
        if not self._thread:
            self.message_signal.emit("串口未打开")
            return False
        
        return self._thread.write_command(identifier, data_list)

    def is_connected(self) -> bool:
        """检查连接状态"""
        return (self._serial_port is not None and 
                self._serial_port.is_open and 
                self._thread is not None and 
                self._thread.isRunning())

    def _cleanup(self):
        """内部清理"""
        self._serial_port = None
        self._thread = None
        